/*
 * sysmo 服务器等待加入监控列表的新客户端（节点）
 * Copyright (C) 2009  YuLicheng
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.sysmo.server.node;

import org.sysmo.communication.CommunicationInterface;
import org.sysmo.communication.CommunicationProtocol;
import org.sysmo.db.DatabaseInterface;
import org.sysmo.db.DbClient;

/**
 * 
 * 此类实例表示一个等待加入监控列表的客户端节点。当用户允许该节点加入监控列表时，此实
 * 例将会生成已连接节点，并且使用回调接口通知外部逻辑。
 *
 * @author YuLicheng
 */
public class NewNode extends Node {

    private CommunicationInterface client;
    private boolean waitingForAddingFlag;
    private Thread waitingThread;

    /**
     * 当新客户端连接时，此类的实例被创建。
     *
     * @param dbInterface 数据库访问接口。
     * @param stateListener 子对象状态监听接口。
     * @param client 客户端通讯协议接口。
     */
    public NewNode(DatabaseInterface dbInterface, 
            NodeStateChangeListener stateListener,
            CommunicationInterface client) {
        super(dbInterface, stateListener);
        this.client = client;
    }

    /**
     * 创建与新的客户端之间的连接，以保证客户端节点一直在等待加入被监控列表。当其被
     * 加入时，此方法即返回，而连接控制权会被转交给新的已连接节点实例。此方法将创建
     * 一个新线程执行等待连接操作，而旧线程会立即返回。
     * <br />
     *
     * 在客户端被加入监控列表前，此方法会忽略客户端发送的所有消息，并且保持与客户端
     * 的连接。但当客户端被加入监控列表后，终止标记被设置，客户端此时应该给服务器发
     * 送任意的字符串，以使得此处的等待客户端消息的方法能够返回，然后此方法即退出。
     */
    public void waitConnect() {
        final NewNode node = this;
        waitingForAddingFlag = true;
        waitingThread = new Thread() {
            @Override
            public void run() {
                try {
                    while (waitingForAddingFlag) {
                        client.getString();
                    }
                } catch (Exception e) {
                    client.closeConnection();
                    if (waitingForAddingFlag) {
                        stateListener.newNodeDisconnected(node,
                                NodeStateChangeListener.CLIENT_CONNECTION_ABORT);
                    }
                }
            }
        };
        waitingThread.start();
    }

    /**
     * 终止与新客户端的连接。一般只有当服务器需要关闭时才会调用此方法。此方法会发送
     * 服务器已关闭的消息给客户端并断开连接。导致{@link #waitConnect}中的等待线程
     * 结束。
     */
    public void stopWaitConnect() {
        try {
            client.sendString(CommunicationProtocol.encapsulate(3, ""));
        } catch (Exception e) {
            e.printStackTrace();
        }
        client.closeConnection();
        try {
            waitingThread.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        stateListener.newNodeDisconnected(this,
                NodeStateChangeListener.CLIENT_SHUTDOWN);
    }

    /**
     * 将此节点加入被监控列表。
     *
     * 当用户选择将此节点加入被监控列表时，调用此方法。此方法会给客户端发送一个新的
     * 客户端ID，然后等待客户端发送任意消息结束{@link #waitConnect()}中的等待线程，
     * 并生成此节点的{@link ConnectedNode}实例。最后调用节点状态回调接口的
     * {@link NodeStateChangeListener#nodeAdded(NewNode, ConnectedNode)}方法通
     * 知外部逻辑后返回。
     *
     * @param name 为客户端节点指定的别名。
     * @return 此客户端对应的已连接节点实例。
     * @see NodeStateChangeListener
     */
    public ConnectedNode addIn(String name) {
        DbClient node = new DbClient();
        node.cid = 0;
        node.name = name;
        try {
            dbInterface.update_ClientTable(node);
            client.sendString(CommunicationProtocol.encapsulateWithItemID(
                    1, node.cid, ""));
        } catch (Exception e) {
            e.printStackTrace();
        }
        waitingForAddingFlag = false;
        try {
            waitingThread.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
        ConnectedNode newNode = null;
        try {
            newNode = new ConnectedNode(stateListener, dbInterface, client, node.cid);
        } catch (Exception e) {
            e.printStackTrace();
        }
        stateListener.nodeAdded(this, newNode);
        return newNode;
    }
}
